﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq.Expressions;
using System.Reflection;
using System.Linq;
using Framework.Extensions;
using Framework.Model;
using Framework.Sql;

namespace Framework.Metadata
{
    public class LazyLoadPropertyMetadata : PropertyMetadataBase
    {
        public LazyLoadPropertyMetadata(PropertyInfo propertyInfo)
        {
            PropertyInfo = propertyInfo;
        }

        private Type GetItemType()
        {
            return PropertyInfo
                .PropertyType
                .GetArgumentsOfGenericBaseType(typeof(IEnumerable<>))
                .Single();
        }

        public override void UpdateObject(object o, IDataRecord record, Func<Type, LambdaExpression, Action<SelectSqlBuilder, Type>, IEnumerable> find)
        {
            var itemType = GetItemType();

            var id = ((IId) o).Id;
            var idProperty = typeof (IId).GetProperties().Single();
// ReSharper disable AccessToModifiedClosure
            idProperty = o.GetType().GetProperties().Single(p => p.Name == idProperty.Name);
// ReSharper restore AccessToModifiedClosure

            var propertyMetadata = (ColumnPropertyMetadata) FromProperty(idProperty);

            var results = find(itemType, null, (sqlBuilder, type) => sqlBuilder.AddCondition(string.Format("{0} = {1}", propertyMetadata.ColumnName, id)));

            if (!PropertyInfo.CanWrite)
            {
                var fieldSetter = PropertyInfo.ReflectedType.GetField(
                    "_" + PropertyInfo.Name[0].ToString().ToLower() + PropertyInfo.Name.Substring(1),
                    BindingFlags.NonPublic | BindingFlags.Instance);
                fieldSetter.SetValue(o, results);
            }
            else
                PropertyInfo.SetValue(o, results, null);
        }

        public override void SaveObject(Action<object, Action<InsertSqlBuilder>> saveMethod, object entity, Action<Type, PropertyInfo, int, IEnumerable<int>> delete = null)
        {
            var idProperty = typeof (IId).GetProperties().Single();
// ReSharper disable AccessToModifiedClosure
            idProperty = entity.GetType().GetProperties().Single(p => p.Name == idProperty.Name);
// ReSharper restore AccessToModifiedClosure
            var parentColumnName = ((ColumnPropertyMetadata)FromProperty(idProperty)).ColumnName;

            var parentId = ((IId) entity).Id;
            var list = ((IEnumerable) PropertyInfo.GetValue(entity, null)).Cast<IId>();
            if (delete != null && list.Any())
            {
                var itemType = GetItemType();
                delete(itemType, idProperty, parentId, list.Select(iid => iid.Id));
            }

            foreach (var item in list)
                saveMethod(item, builder => builder.AddUpdateColumn(parentColumnName, parentId));
        }
    }
}